// ChimaeraSVN

// Copyright (C) 2008-2009 - ChimaeraSVN

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
#include "StdAfx.h"

#include "SVN.h"
#include "svn_cmdline.h"
#include "svn_hash.h"

#include "openssl/x509.h"
#include "openssl/pem.h"


#define _(str) (str)


svn_string_t* SVN::user_ID=NULL;
svn_revnum_t	SVN::recno=0;
char * SVN::privilege=NULL;
char * SVN::user_name=NULL;


SVN::SVN(void)
{
	svn_cmdline_init ("minimal_client", stderr);
	parentpool = svn_pool_create(NULL);
	svn_error_clear(svn_client_create_context(&m_pctx, parentpool));

	Err = svn_config_ensure(NULL, parentpool);
	pool = svn_pool_create (parentpool);
	// set up the configuration
	if (Err == 0)
		Err = svn_config_get_config (&(m_pctx->config), NULL, parentpool);

	if (Err != 0)
	{
		svn_handle_error2 (Err, stderr, FALSE, "SVN Class Init: ");
		svn_pool_destroy (pool);
		svn_pool_destroy (parentpool);
		exit(-1);
	}

	m_pctx->client_name = apr_pstrdup(pool, _T("ChimaeraSVN-0.1beta"));
	m_prompt.Init(parentpool, m_pctx);
	svn_auth_set_parameter(m_pctx->auth_baton, SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
	svn_auth_set_parameter(m_pctx->auth_baton, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");
}

SVN::~SVN(void)
{
	svn_error_clear(Err);
	svn_pool_destroy (parentpool);
	m_prompt.~SVNPrompt();
}

svn_string_t * SVN::get_userID(char *filename)
{
	FILE *cafile;
	apr_int32_t cert_len;
	void *certificate_from_localfile;

	X509 *x509certificate=NULL;
	ASN1_INTEGER *op=0;

	cafile = fopen(filename,"rb");

	if (cafile == NULL)
	{
		printf("file doesn't exist, check the path!");
		return 0;
	}

	if (!fseek(cafile,0,SEEK_END))
	{
		cert_len = ftell(cafile);
		if (cert_len == -1)
		{
			return 0;
		}
		certificate_from_localfile = malloc(cert_len);
		fseek(cafile,0,SEEK_SET);
		fread(certificate_from_localfile,cert_len,1,cafile);
	}

	//BIO* cert_bio = BIO_new_mem_buf(certificate_from_localfile,cert_len);


	/* USERID */
	fseek(cafile,0,SEEK_SET);
	if (1)
	{
		/* support postfix .pem .der*/
		char *cmp1 = ".pem";
		char *cmp2 = ".der";

		if(!strcmp(cmp1,filename+strlen(filename)-4))
			x509certificate = 
			PEM_read_X509(cafile,NULL,NULL,NULL);
			//PEM_read_bio_X509(cert_bio,NULL,NULL,NULL);
		else if(!strcmp(cmp2,filename+strlen(filename)-4))
			x509certificate = d2i_X509_fp(cafile, NULL);
		else
		{
			printf("only .der or .pem file is supported!\n");
			return 0;
		}

	}
	op = X509_get_serialNumber(x509certificate);

	
	svn_string_t *temp = (svn_string_t *)malloc(sizeof(*temp));
	temp->len=op->length;
	temp->data = (const char*)malloc(temp->len);
	memcpy((void *)temp->data,op->data,op->length);

	//if(cert_bio!=NULL){
		//BIO_free(cert_bio);
		//cert_bio = NULL;
	//}


	//I don't know why there is always a memory leak ...
	if(x509certificate!=NULL){
		X509_free(x509certificate);
		x509certificate = NULL;
	}


	free(certificate_from_localfile);
	fclose(cafile);
	return temp;
}

BOOL SVN::List(const char *path_or_url, svn_boolean_t recurse)
{
	//apr_pool_t *subpool = svn_pool_create(pool);
	
	svn_error_clear(Err);
	Err = NULL;
	
	rev.kind = svn_opt_revision_head;

	Err = svn_client_ls(&dirents,
						  path_or_url,
						  &rev,
						  recurse,
						  m_pctx,
						  pool);
	if (Err != NULL)
		return FALSE;

	

	return TRUE;
}

time_t sscanftime(const char* ptszTime)
{
	time_t    tResult        = 0;
	struct tm tmResult    = {0};
	int iYear    = 0;
	int iMonth    = 1;
	int iDay    = 0;
	int iHour    = 0;
	int iMinut    = 0;
	int iSecon    = 0;
	_stscanf(ptszTime,"%d-%d-%d",&iYear,&iMonth,&iDay);

	tmResult.tm_hour    = iHour;
	tmResult.tm_isdst    = 0;
	tmResult.tm_min        = iMinut;
	tmResult.tm_mon        = iMonth - 1;
	tmResult.tm_sec        = iSecon;
	tmResult.tm_mday    = iDay;
	tmResult.tm_year    = iYear - 1900;

	tResult = mktime(&tmResult);

	return tResult;
}


BOOL SVN::CreateProject(const char *path_or_url,char *project_name,
						char *groupleader, char ** time_nodes_str,
						apr_byte_t node_num, char *projinfo,
						svn_boolean_t recurse)
{
	//apr_pool_t *subpool = svn_pool_create(pool);
	int i;
	char **temp;
	apr_time_t *nodes,*result;
	time_t tm_t;
	char debug_time[254];


	char * rootpath = (char *)apr_pcalloc(pool,strlen(project_name)+2);
	sprintf(rootpath,"/%s",project_name);

	nodes = (apr_time_t *)apr_pcalloc(pool,sizeof(*nodes)*node_num);
	result = nodes;
	
	for (i=0,temp=time_nodes_str;i<node_num;i++,temp++,result++)
	{
		tm_t = sscanftime(*temp);
		apr_time_ansi_put(result,tm_t);
		//memcpy(point,result,sizeof(*point));
		apr_ctime(debug_time,*result);
		printf("%s\n",debug_time);
	}
	

	svn_error_clear(Err);
	Err = NULL;
	
	//if (SVN::recno==0)
	{
		this->getuserinfo(path_or_url,true);
	}
	if (!strcmp(SVN::privilege,"manager"))
	{
		Err = svn_client_chimaera_create_project(project_name,
			rootpath,
			projinfo,
			groupleader,
			"0",
			nodes,
			node_num,
			path_or_url,
			m_pctx,
			pool);
		if (Err != NULL)
			return FALSE;
		return TRUE;
	}
	else{
		Err = svn_error_create(9999,NULL,_("no access"));
		return FALSE;
	}
	



	
}
BOOL SVN::getuserinfo(const char *path_or_url, svn_boolean_t recurse)
{
	
	Err = svn_client_chimaera_get_userinfo(&SVN::user_name,
		&SVN::privilege,
		&SVN::recno,
		user_ID,
		path_or_url,
		m_pctx,
		pool);
	if (Err != NULL)
		return FALSE;



	return TRUE;
}
BOOL SVN::get_new_message(chimaeramessage_t **message,
						  apr_int32_t *num,
						  const char *path_or_url)
{
	/* user record number, the only param need*/
	apr_int32_t user_recno=SVN::recno;

	Err = svn_client_chimaera_get_newmessage(message,
		num,
		user_recno,
		path_or_url,
		m_pctx,
		pool);
	if (Err != NULL)
		return FALSE;



	return TRUE;
}

BOOL SVN::import_file(const char *source_path, char * des_path, 
					  svn_boolean_t recurse)
{
	svn_commit_info_t *commit_info = NULL;
	apr_array_header_t *targets=NULL;

	svn_error_clear(Err);
	Err = NULL;

	//ft, can't import files to an exist folder in server
	//Err = svn_client_import2(&commit_info,
		//path, url, recurse, FALSE, m_pctx, pool);

	//so, just copy, add then commit ...

	//check whether the file already exists
	//get filename
	/*char *temp;
	int i,j;
	j=strlen(path);
	for (i=j,temp=path;i>0;i--)
	{
		if(*(temp+i-1)=='\\')
			break;
	}*/

	char *temp;
	temp = strrchr(source_path,'\\');
	if (temp == NULL)
		temp = (char *)source_path;
	else
		temp++;
	
	char *wc = des_path;
	char *des = (char *)calloc(strlen(temp)+strlen(wc)+2,1);
	strcat(des,wc);
	strcat(des,"\\");
	strcat(des,temp);


	BOOL cres;
	cres = CopyFile(source_path,des,FALSE);


	Err = svn_client_add4(des, svn_depth_infinity, TRUE, FALSE, TRUE, m_pctx, pool);

	targets = apr_array_make(pool,1,sizeof(char *));
	(*((const char **) apr_array_push(targets))) = wc;

	Err = svn_client_commit3(&commit_info,
		targets, recurse, TRUE, m_pctx, pool);



	free(des);
	if (Err != NULL)
		return FALSE;



	return TRUE;
}

BOOL SVN::update(const char *URL,const char *path, apr_int32_t sign,
				 svn_boolean_t recurse)
{
	apr_array_header_t *targets=NULL;

	int argc=3;
	const char *argv[3];
	apr_getopt_t *os;


	argv[0] = "pp.exe";
	argv[1] = "up";
	argv[2] = path;

	svn_error_clear(Err);
	Err = NULL;

	svn_revnum_t latest_revnum;
	
	svn_cmdline__getopt_init(&os, argc, argv, pool);
	os->ind=2;
	svn_opt_args_to_target_array3(&targets, os,
		NULL, pool);
	svn_opt_push_implicit_dot_target(targets, pool);

	//targets = apr_array_make(pool,1,sizeof(char *));
	//(*((const char **) apr_array_push(targets))) = path;
	if (sign == -1)
	{
		rev.kind = svn_opt_revision_head;
	}
	else if (sign == 0)
	{
		get_latest_revnum(&latest_revnum,URL);
		rev.kind = svn_opt_revision_number;
		rev.value.number=latest_revnum-1;

	}else{
		rev.kind = svn_opt_revision_number;
		rev.value.number=sign;
	}
	

	Err = svn_client_update3(NULL,targets, &rev, svn_depth_unknown,
				FALSE,FALSE,FALSE, m_pctx, pool);

	if (Err != NULL)
		return FALSE;



	return TRUE;


}
BOOL SVN::remove(const char *path, svn_boolean_t force)
{
	apr_array_header_t *targets=NULL;
	svn_commit_info_t *commit_info = NULL;



	int argc=3;
	const char *argv[3];
	apr_getopt_t *os;


	argv[0] = "pp.exe";
	argv[1] = "up";
	argv[2] = path;

	svn_error_clear(Err);
	Err = NULL;

	svn_cmdline__getopt_init(&os, argc, argv, pool);
	os->ind=2;
	svn_opt_args_to_target_array3(&targets, os,
		NULL, pool);
	svn_opt_push_implicit_dot_target(targets, pool);

	rev.kind = svn_opt_revision_unspecified;


	Err = svn_client_delete3(&commit_info,targets,
		force,FALSE,NULL, m_pctx, pool);

	if (Err != NULL)
		return FALSE;

	Err = svn_client_commit3(&commit_info,
		targets, FALSE, TRUE, m_pctx, pool);

	if (Err != NULL)
		return FALSE;



	return TRUE;


}

BOOL SVN::getuserlist(chimaerauserlist_t **user_list,
					  apr_int32_t *num,apr_int32_t sign,
					  const char *para,const char *path_or_url)
{
	Err = svn_client_chimaera_get_userlist(user_list,
		num,
		sign,
		para,
		path_or_url,
		m_pctx,
		pool);
	if (Err != NULL)
		return FALSE;



	return TRUE;
}

BOOL SVN::editcolumns(apr_int32_t sign,
					  svn_string_t *key,
					  svn_string_t *value1,
					  svn_string_t *value2,
					  svn_string_t *value3,
					  const char *path_or_url)
{
	Err = svn_client_chimaera_edit_columns(sign,
		key,
		value1,
		value2,
		value3,
		path_or_url,
		m_pctx,
		pool);
	if (Err != NULL)
		return FALSE;



	return TRUE;
}

BOOL SVN::getcolumns(apr_int32_t *group_recno,
				apr_int32_t sign,
				svn_string_t *key,
				svn_string_t *value1,
				const char *path_or_url)
{
	Err = svn_client_chimaera_get_columns(
		group_recno,
		sign,
		key,
		value1,
		path_or_url,
		m_pctx,
		pool);
	if (Err != NULL)
		return FALSE;



	return TRUE;
}

BOOL SVN::save(const char *path, svn_boolean_t force)
{
	svn_commit_info_t *commit_info = NULL;
	apr_array_header_t *targets=NULL;
	
	targets = apr_array_make(pool,1,sizeof(char *));
	(*((const char **) apr_array_push(targets))) = path;

	Err = svn_client_commit3(&commit_info,
		targets, force, TRUE, m_pctx, pool);

	if (Err != NULL)
		return FALSE;



	return TRUE;
}

BOOL SVN::lock(const char *path, svn_boolean_t steal_lock)
{
	char *comment = "a file locked";
	apr_array_header_t *targets=NULL;

	targets = apr_array_make(pool,1,sizeof(char *));
	(*((const char **) apr_array_push(targets))) = path;

	Err = svn_client_lock(targets, comment,
		steal_lock, m_pctx, pool);

	if (Err != NULL)
		return FALSE;



	return TRUE;
}

BOOL SVN::unlock(const char *path, svn_boolean_t break_lock)
{
	apr_array_header_t *targets=NULL;

	targets = apr_array_make(pool,1,sizeof(char *));
	(*((const char **) apr_array_push(targets))) = path;

	Err = svn_client_unlock(targets,
		break_lock, m_pctx, pool);

	if (Err != NULL)
		return FALSE;



	return TRUE;
}

BOOL SVN::get_latest_revnum(svn_revnum_t *latest_revnum,
							const char *path)
{

	svn_error_clear(Err);
	Err = NULL;
	Err = svn_client_chimaera_get_latestrev(latest_revnum,
		path,
		m_pctx,
		pool);

	if(Err != NULL)
	{
		return FALSE;
	}

	return TRUE;
}

BOOL SVN::Checkout(const char *URL, const char *path, 
						  apr_int32_t sign,svn_boolean_t recurse)
{
	apr_pool_t *subpool = svn_pool_create(pool);
	svn_revnum_t latest_revnum;

	/* previous version */
	if (sign == -1)
	{
		rev.kind = svn_opt_revision_head;
	}
	else if (sign == 0)
	{
		get_latest_revnum(&latest_revnum,URL);
		rev.kind = svn_opt_revision_number;
		rev.value.number=latest_revnum-1;
		
	}else{
		rev.kind = svn_opt_revision_number;
		rev.value.number=sign;
	}

	

	svn_error_clear(Err);
	Err = NULL;
	Err = svn_client_checkout3(NULL,			// we don't need the resulting revision
		URL,
		path,&rev,
		&rev,
		svn_depth_empty,FALSE, FALSE,
		m_pctx,
		subpool);

	if(Err != NULL)
	{
		return FALSE;
	}
	svn_pool_destroy(subpool);
	return TRUE;

}
BOOL SVN::RemoveProject(const char *url, svn_boolean_t force)
{
	apr_array_header_t *targets=NULL;
	svn_commit_info_t *commit_info = NULL;



	int argc=3;
	const char *argv[3];
	apr_getopt_t *os;


	argv[0] = "pp.exe";
	argv[1] = "up";
	argv[2] = url;

	svn_error_clear(Err);
	Err = NULL;

	svn_cmdline__getopt_init(&os, argc, argv, pool);
	os->ind=2;
	svn_opt_args_to_target_array3(&targets, os,
		NULL, pool);
	svn_opt_push_implicit_dot_target(targets, pool);

	rev.kind = svn_opt_revision_unspecified;


	Err = svn_client_chimaera_remove_project(url,
		m_pctx,
		pool);

	if (Err != NULL)
		return FALSE;

	

	Err = svn_client_delete3(&commit_info,targets,
		force,FALSE,NULL, m_pctx, pool);


	return TRUE;


}

BOOL SVN::InsertUser(char *username, apr_int32_t character, 
				char *certificate_file_path,  
				char *userinfo,	const char *url)
{
	FILE *cafile;
	apr_int32_t cert_len;
	void *certificate_from_localfile;
	svn_string_t *certificate;


	X509 *x509certificate;
	ASN1_INTEGER *op=0;

	cafile = fopen(certificate_file_path,"rb");

	if (cafile == NULL)
	{
		Err = svn_error_create(9999,NULL,
			_("file doesn't exist, check the path!"));
		return FALSE;
	}

	if (!fseek(cafile,0,SEEK_END))
	{
		cert_len = ftell(cafile);
		if (cert_len == -1)
		{
			Err = svn_error_create(9999,NULL,
				_("read file problems, check the file!"));
			return FALSE;
		}
		certificate_from_localfile = apr_palloc(pool,cert_len);
		fseek(cafile,0,SEEK_SET);
		fread(certificate_from_localfile,cert_len,1,cafile);
	}

	certificate = (svn_string_t *)apr_pcalloc(pool, sizeof(*certificate));
	certificate->data=(char *)certificate_from_localfile;
	certificate->len=cert_len;

	/* USERID */
	fseek(cafile,0,SEEK_SET);
	if (1)
	{
		/* support postfix .pem .der*/
		char *cmp1 = ".pem";
		char *cmp2 = ".der";

		if(!strcmp(cmp1,
			certificate_file_path+strlen(certificate_file_path)-4))
			x509certificate = PEM_read_X509(cafile,NULL,NULL,NULL);
		//PEM_read_bio_X509(cert_bio,NULL,NULL,NULL);
		else if(!strcmp(cmp2,
			certificate_file_path+strlen(certificate_file_path)-4))
			x509certificate = d2i_X509_fp(cafile, NULL);
		else
		{
			Err = svn_error_create(9999,NULL,
				_("only .der or .pem file is supported!"));
			return FALSE;
		}

	}
	op = X509_get_serialNumber(x509certificate);


	svn_string_t *temp = (svn_string_t *)apr_pcalloc(pool,sizeof(*temp));
	temp->len=op->length;
	temp->data = (const char*)apr_pcalloc(pool,temp->len);
	memcpy((void *)temp->data,op->data,op->length);

	

	Err = svn_client_chimaera_insert_user(username,
		temp,
		character,
		certificate,
		userinfo,
		url,
		m_pctx,
		pool);

	if(x509certificate!=NULL){
		X509_free(x509certificate);
		x509certificate = NULL;
	}


	fclose(cafile);

	if (Err != NULL)
		return FALSE;

	return TRUE;
}

BOOL SVN::RemoveUser(svn_revnum_t user_recno, const char *url)
{
	Err = svn_client_chimaera_remove_user(user_recno,
		url, m_pctx, pool);

	if (Err != NULL)
		return FALSE;

	return TRUE;
}

BOOL SVN::EditUser(svn_revnum_t user_recno, char *username,
			  apr_int32_t character, char *path_or_null,  
			  char *userinfo, char * url)
{
	FILE *cafile;
	apr_int32_t cert_len;
	void *certificate_from_localfile;
	svn_string_t *certificate;
	svn_string_t *temp;


	X509 *x509certificate=NULL;
	ASN1_INTEGER *op=0;

	certificate = (svn_string_t *)apr_pcalloc(pool, sizeof(*certificate));
	temp = (svn_string_t *)apr_pcalloc(pool,sizeof(*temp));

	if (strlen(path_or_null) == 0)
	{
		certificate->data="";
		certificate->len=0;

		temp->len=0;
		temp->data = "";
	}
	else{
		cafile = fopen(path_or_null,"rb");

		if (cafile == NULL)
		{
			Err = svn_error_create(9999,NULL,
				_("file doesn't exist, check the path!"));
			return FALSE;
		}

		if (!fseek(cafile,0,SEEK_END))
		{
			cert_len = ftell(cafile);
			if (cert_len == -1)
			{
				Err = svn_error_create(9999,NULL,
					_("read file problems, check the file!"));
				return FALSE;
			}
			certificate_from_localfile = apr_palloc(pool,cert_len);
			fseek(cafile,0,SEEK_SET);
			fread(certificate_from_localfile,cert_len,1,cafile);
		}


		certificate->data=(char *)certificate_from_localfile;
		certificate->len=cert_len;

		/* USERID */
		fseek(cafile,0,SEEK_SET);
		if (1)
		{
			/* support postfix .pem .der*/
			char *cmp1 = ".pem";
			char *cmp2 = ".der";

			if(!strcmp(cmp1,
				path_or_null+strlen(path_or_null)-4))
				x509certificate = PEM_read_X509(cafile,NULL,NULL,NULL);
			//PEM_read_bio_X509(cert_bio,NULL,NULL,NULL);
			else if(!strcmp(cmp2,
				path_or_null+strlen(path_or_null)-4))
				x509certificate = d2i_X509_fp(cafile, NULL);
			else
			{
				Err = svn_error_create(9999,NULL,
					_("only .der or .pem file is supported!"));
				return FALSE;
			}

		}
		op = X509_get_serialNumber(x509certificate);


		temp->len=op->length;
		temp->data = (const char*)apr_pcalloc(pool,temp->len);
		memcpy((void *)temp->data,op->data,op->length);

		fclose(cafile);
	}


	Err = svn_client_chimaera_edit_user(user_recno,
		username,
		temp,
		character,
		certificate,
		userinfo,
		url,
		m_pctx,
		pool);

	if(x509certificate!=NULL){
		X509_free(x509certificate);
		x509certificate = NULL;
	}
		

	if (Err != NULL)
		return FALSE;

	return TRUE;
}
